iT邦幫忙

2024 iThome 鐵人賽

DAY 10
0
Modern Web

Rust 的戰國時代:探索網頁前端工具的前世今生系列 第 10

Day 10:用實驗來理解 Vite 的 dependency pre-bundling 是什麼 (1)

  • 分享至 

  • xImage
  •  

day 10 banner

(Photo by Jr Korpa)

前言

昨天在探討為什麼 Vite 在 dev server 的冷啟動這麼快時,目前我們只知道是因為有用上了瀏覽器的 native ESM 去省略打包的時間,但要做到這件事其實會需要處理蠻多問題,整個重點整理的話是利用以下幾點來實現:

  • 依賴模組的預構建 (dependency pre-bundling)
    • 模組化規範的兼容
    • 效能優化
  • 複寫模組路徑
  • 改使用 esbuild 做語法轉譯
  • 快取機制
    • 檔案系統快取
    • 瀏覽器快取

光看文字可能沒那麼好懂,或許學習最快的方式就是動手做,千萬不要像我一樣前面講了八天的歷史故事光說不練。今天就實際來做實驗吧!

先附上今天做實驗的程式碼初始模板供參考。

實驗 1. 載入第三方 ESM 套件

快速啟一個實驗專案:

mkdir esm-demo
cd esm-demo
pnpm init
pnpm add lodash-es

接著在根目錄上新增以下兩個檔案:

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>ESM Demo</title>
  </head>

  <body>
    <script type="module" src="main.js"></script>
  </body>
</html>

main.js

import { has } from 'lodash-es';

console.log(has({ a: 1 }, 'a'));

啟用 live server

此時如果你是 VS Code 的使用者,你可以安裝 Live Server 這個擴充套件,這是方便我們在本機直接 host 我們的網頁起來的工具,安裝後你可以對剛剛建立好的 index.html 去點右鍵並選擇「Open with Live Server」:

live server

應該能自動打開瀏覽器畫面:

lab 1 result

當你打開 dev tool 時,會看到 console 上印出這個錯誤:

Uncaught TypeError: Failed to resolve module specifier "lodash-es". Relative references must start with either "/", "./", or "../".

解法

這是為什麼呢?這個錯誤訊息的意思是說無法解析到 lodash-es 這個檔案。當你想使用 native ESM 的寫法用 <script type=”module”> 的方式來載入你的 ESM 模組時,在載入模組時,瀏覽器需要絕對或相對路徑才能識別檔案,因此當你今天將上面 main.js 的引入路徑改成這樣就會正常了:

import { has } from './node_modules/lodash-es/lodash.js';

我們先記住這個結果,再來看另一個實驗。

實驗 2. 載入有大型依賴關係的第三方套件

觀察 request 數量

光看文字可能很難懂,延續上一個實驗,其實基本上已經做完了,當你上一步最後的 main.js 的內容是這個樣子的時候:

import { has } from './node_modules/lodash-es/lodash.js';

console.log(has({ a: 1 }, 'a'));

我們這裡試著用 named import 的方式引入了 has 這個模組,當你這時用 Live Server 去將網頁啟起來後,打開 dev tool 後重整網頁,去觀察 network 中的 JS 載入數量:

request waterfall

你會看到一大串的 JS request 如瀑布一樣載進來,總共載了 600 多個,載入完成後 console 才印出 true 這個結果。

觀察引入的模組原始碼

這是為什麼呢?如果你去看 node_modules/lodash-es/lodash.js 的這個檔案,會看到它是負責 export 其他子模組的主要入口檔。再追進去 has.js 這支檔案中,會看到裡面還引入了 _hasPath,而其中又會如下圖一樣層層引入其他模組:

hasPath

透過這個實驗可以發現,當今天是用 native ESM 的方式在瀏覽器上載入模組時,可能會因為用到的這個模組背後有上百、上千個依賴小模組而觸發過多的 request,造成頁面載入的卡頓,這反而會成為開發體驗上的另一個問題。而這也正是為何要做 dependency pre-bundling 的一個主因,一樣先記住這個結果,明天會再實際來看看 Vite 怎麼處理。

一個小發現

另外這邊有個有趣的發現,當你今天將 main.js 改成用 default import 直接載入 has 的話:

import has from './node_modules/lodash-es/has.js';

再去實驗看看 JS 模組的 request 數量,會從 600 多個降到剩下 60 多個,才知道原來原生的行為就算執行的檔案只有對其中的某一個模組做 named import,實際還是會去分析所有的模組依賴,看來 dev server 真的幫忙做了很多優化。

註:第二個實驗範例是參考高見龍在這個影片 demo 的靈感,選擇用 has 這個模組看起來就是為了感受一下大型依賴的感覺。

小結

今天我們實際用 lodash-es 純手工實驗了下要在 native ESM 的瀏覽器環境下載入第三方套件可能會遇到什麼問題,明天我們將繼續來做相關的實驗,最後再統一做 Vite 實現原理的重點說明!

參考資源


上一篇
Day 09:為什麼 Vite 的冷啟動可以這麼快?
下一篇
Day 11:用實驗來理解 Vite 的 dependency pre-bundling 是什麼 (2)
系列文
Rust 的戰國時代:探索網頁前端工具的前世今生12
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言